"""
File Service for Desktop Application
桌面应用文件服务
"""

import logging
import asyncio
import aiofiles
import aiohttp
from typing import Optional, Dict, Any, List, Callable
from pathlib import Path
import mimetypes
import hashlib

from ..services.api_client import APIClient
from ..core.state_manager import StateManager
from flet import Page

logger = logging.getLogger(__name__)


class FileService:
    """文件服务类"""
    
    def __init__(self, page: Page, api_client: APIClient, state_manager: StateManager):
        self.page = page
        self.api_client = api_client
        self.state_manager = state_manager
        
        # 文件上传/下载状态
        self.upload_tasks: Dict[str, Dict[str, Any]] = {}
        self.download_tasks: Dict[str, Dict[str, Any]] = {}
        
        # 回调函数
        self.upload_callbacks: List[Callable] = []
        self.download_callbacks: List[Callable] = []
        
        # 配置
        self.chunk_size = 1024 * 1024  # 1MB
        self.max_concurrent_uploads = 3
        self.max_concurrent_downloads = 3
        
    async def upload_file(
        self,
        file_path: str,
        destination_path: str,
        metadata: Optional[Dict[str, Any]] = None,
        progress_callback: Optional[Callable] = None
    ) -> Dict[str, Any]:
        """
        上传文件
        
        Args:
            file_path: 本地文件路径
            destination_path: 目标路径
            metadata: 文件元数据
            progress_callback: 进度回调
            
        Returns:
            Dict[str, Any]: 上传结果
        """
        task_id = f"upload_{hashlib.md5(file_path.encode()).hexdigest()}"
        
        try:
            logger.info(f"Starting file upload: {file_path}")
            
            # 验证文件
            file_path_obj = Path(file_path)
            if not file_path_obj.exists():
                raise FileNotFoundError(f"文件不存在: {file_path}")
            
            if not file_path_obj.is_file():
                raise ValueError("路径不是文件")
            
            file_size = file_path_obj.stat().st_size
            file_name = file_path_obj.name
            mime_type, _ = mimetypes.guess_type(file_path)
            
            # 创建上传任务
            upload_task = {
                "task_id": task_id,
                "file_path": file_path,
                "destination_path": destination_path,
                "file_name": file_name,
                "file_size": file_size,
                "mime_type": mime_type,
                "metadata": metadata or {},
                "progress": 0,
                "status": "pending",
                "error": None,
                "start_time": None,
                "end_time": None,
            }
            
            self.upload_tasks[task_id] = upload_task
            
            # 通知回调
            await self._notify_upload_callbacks("task_started", upload_task)
            
            # 获取上传URL
            upload_response = await self._get_upload_url(
                file_name, file_size, mime_type, metadata
            )
            
            if not upload_response.get("success"):
                raise Exception(f"获取上传URL失败: {upload_response.get('error', {}).get('message', '未知错误')}")
            
            upload_data = upload_response.get("data", {})
            upload_url = upload_data.get("upload_url")
            
            if not upload_url:
                raise Exception("未获取到上传URL")
            
            # 执行上传
            upload_task["start_time"] = asyncio.get_event_loop().time()
            upload_task["status"] = "uploading"
            
            result = await self._perform_upload(
                task_id, file_path, upload_url, progress_callback
            )
            
            upload_task["end_time"] = asyncio.get_event_loop().time()
            upload_task["status"] = "completed"
            upload_task["progress"] = 100
            
            logger.info(f"File upload completed: {file_path}")
            
            # 通知回调
            await self._notify_upload_callbacks("task_completed", upload_task)
            
            return {
                "success": True,
                "task_id": task_id,
                "file_path": result.get("file_path", destination_path),
                "file_size": file_size,
                "upload_time": upload_task["end_time"] - upload_task["start_time"],
            }
            
        except Exception as e:
            logger.error(f"File upload failed: {e}")
            
            if task_id in self.upload_tasks:
                self.upload_tasks[task_id]["status"] = "failed"
                self.upload_tasks[task_id]["error"] = str(e)
                await self._notify_upload_callbacks("task_failed", self.upload_tasks[task_id])
            
            return {
                "success": False,
                "task_id": task_id,
                "error": str(e),
            }
    
    async def download_file(
        self,
        file_url: str,
        local_path: str,
        progress_callback: Optional[Callable] = None
    ) -> Dict[str, Any]:
        """
        下载文件
        
        Args:
            file_url: 文件URL
            local_path: 本地保存路径
            progress_callback: 进度回调
            
        Returns:
            Dict[str, Any]: 下载结果
        """
        task_id = f"download_{hashlib.md5(file_url.encode()).hexdigest()}"
        
        try:
            logger.info(f"Starting file download: {file_url}")
            
            # 创建下载任务
            download_task = {
                "task_id": task_id,
                "file_url": file_url,
                "local_path": local_path,
                "progress": 0,
                "status": "pending",
                "error": None,
                "start_time": None,
                "end_time": None,
            }
            
            self.download_tasks[task_id] = download_task
            
            # 通知回调
            await self._notify_download_callbacks("task_started", download_task)
            
            # 执行下载
            download_task["start_time"] = asyncio.get_event_loop().time()
            download_task["status"] = "downloading"
            
            result = await self._perform_download(
                task_id, file_url, local_path, progress_callback
            )
            
            download_task["end_time"] = asyncio.get_event_loop().time()
            download_task["status"] = "completed"
            download_task["progress"] = 100
            
            logger.info(f"File download completed: {file_url}")
            
            # 通知回调
            await self._notify_download_callbacks("task_completed", download_task)
            
            return {
                "success": True,
                "task_id": task_id,
                "local_path": result.get("local_path", local_path),
                "file_size": result.get("file_size", 0),
                "download_time": download_task["end_time"] - download_task["start_time"],
            }
            
        except Exception as e:
            logger.error(f"File download failed: {e}")
            
            if task_id in self.download_tasks:
                self.download_tasks[task_id]["status"] = "failed"
                self.download_tasks[task_id]["error"] = str(e)
                await self._notify_download_callbacks("task_failed", self.download_tasks[task_id])
            
            return {
                "success": False,
                "task_id": task_id,
                "error": str(e),
            }
    
    async def preview_file(self, file_path: str) -> Dict[str, Any]:
        """
        预览文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            Dict[str, Any]: 预览信息
        """
        try:
            logger.info(f"Previewing file: {file_path}")
            
            file_path_obj = Path(file_path)
            if not file_path_obj.exists():
                raise FileNotFoundError(f"文件不存在: {file_path}")
            
            file_size = file_path_obj.stat().st_size
            mime_type, _ = mimetypes.guess_type(file_path)
            
            # 计算文件哈希
            file_hash = await self._calculate_file_hash(file_path)
            
            # 获取文件预览信息
            preview_info = {
                "file_path": file_path,
                "file_name": file_path_obj.name,
                "file_size": file_size,
                "file_size_readable": self._format_file_size(file_size),
                "mime_type": mime_type,
                "file_hash": file_hash,
                "modified_time": file_path_obj.stat().st_mtime,
            }
            
            # 根据文件类型生成预览
            if mime_type and mime_type.startswith("image/"):
                preview_info["preview_type"] = "image"
                preview_info["preview_data"] = file_path  # 本地图片路径
            elif mime_type and mime_type.startswith("video/"):
                preview_info["preview_type"] = "video"
                preview_info["preview_data"] = file_path  # 本地视频路径
            elif mime_type and mime_type.startswith("audio/"):
                preview_info["preview_type"] = "audio"
                preview_info["preview_data"] = file_path  # 本地音频路径
            elif mime_type and mime_type.startswith("text/"):
                preview_info["preview_type"] = "text"
                # 读取文本内容的前几行
                preview_info["preview_data"] = await self._read_text_preview(file_path)
            else:
                preview_info["preview_type"] = "file"
                preview_info["preview_data"] = None
            
            logger.info(f"File preview generated: {file_path}")
            
            return {
                "success": True,
                "preview_info": preview_info,
            }
            
        except Exception as e:
            logger.error(f"File preview failed: {e}")
            return {
                "success": False,
                "error": str(e),
            }
    
    async def get_file_info(self, file_path: str) -> Dict[str, Any]:
        """
        获取文件信息
        
        Args:
            file_path: 文件路径
            
        Returns:
            Dict[str, Any]: 文件信息
        """
        try:
            file_path_obj = Path(file_path)
            if not file_path_obj.exists():
                raise FileNotFoundError(f"文件不存在: {file_path}")
            
            stat = file_path_obj.stat()
            mime_type, encoding = mimetypes.guess_type(file_path)
            
            return {
                "success": True,
                "file_info": {
                    "path": file_path,
                    "name": file_path_obj.name,
                    "size": stat.st_size,
                    "size_readable": self._format_file_size(stat.st_size),
                    "mime_type": mime_type,
                    "encoding": encoding,
                    "modified_time": stat.st_mtime,
                    "created_time": stat.st_ctime,
                    "is_directory": file_path_obj.is_dir(),
                    "extension": file_path_obj.suffix,
                }
            }
            
        except Exception as e:
            logger.error(f"Get file info failed: {e}")
            return {
                "success": False,
                "error": str(e),
            }
    
    async def delete_file(self, file_path: str) -> Dict[str, Any]:
        """
        删除文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            Dict[str, Any]: 删除结果
        """
        try:
            file_path_obj = Path(file_path)
            if not file_path_obj.exists():
                raise FileNotFoundError(f"文件不存在: {file_path}")
            
            # 删除文件
            if file_path_obj.is_file():
                file_path_obj.unlink()
            else:
                # 删除目录及其内容
                import shutil
                shutil.rmtree(file_path_obj)
            
            logger.info(f"File deleted: {file_path}")
            
            return {
                "success": True,
                "message": f"文件已删除: {file_path}",
            }
            
        except Exception as e:
            logger.error(f"Delete file failed: {e}")
            return {
                "success": False,
                "error": str(e),
            }
    
    # 私有辅助方法
    async def _get_upload_url(self, file_name: str, file_size: int, mime_type: str, metadata: Dict[str, Any]) -> Dict[str, Any]:
        """获取上传URL"""
        try:
            # 这里应该调用后端API获取上传URL
            # 为了演示，返回一个模拟的URL
            return {
                "success": True,
                "data": {
                    "upload_url": f"https://example.com/upload/{file_name}",
                    "upload_id": f"upload_{hashlib.md5(file_name.encode()).hexdigest()}",
                    "expires_at": "2024-12-31T23:59:59Z",
                }
            }
        except Exception as e:
            logger.error(f"Failed to get upload URL: {e}")
            return {
                "success": False,
                "error": {
                    "message": str(e),
                    "code": "UPLOAD_URL_ERROR"
                }
            }
    
    async def _perform_upload(self, task_id: str, file_path: str, upload_url: str, progress_callback: Optional[Callable]) -> Dict[str, Any]:
        """执行文件上传"""
        try:
            file_path_obj = Path(file_path)
            file_size = file_path_obj.stat().st_size
            
            uploaded_bytes = 0
            
            async with aiofiles.open(file_path, 'rb') as file:
                async with aiohttp.ClientSession() as session:
                    # 模拟分块上传
                    while True:
                        chunk = await file.read(self.chunk_size)
                        if not chunk:
                            break
                        
                        # 模拟上传块（实际应该发送到服务器）
                        await asyncio.sleep(0.1)  # 模拟网络延迟
                        
                        uploaded_bytes += len(chunk)
                        progress = int((uploaded_bytes / file_size) * 100)
                        
                        # 更新任务进度
                        if task_id in self.upload_tasks:
                            self.upload_tasks[task_id]["progress"] = progress
                        
                        # 调用进度回调
                        if progress_callback:
                            if asyncio.iscoroutinefunction(progress_callback):
                                await progress_callback(progress, f"上传中... {progress}%")
                            else:
                                progress_callback(progress, f"上传中... {progress}%")
                        
                        # 通知回调
                        await self._notify_upload_callbacks("progress_updated", {
                            "task_id": task_id,
                            "progress": progress,
                            "uploaded_bytes": uploaded_bytes,
                            "total_bytes": file_size,
                        })
            
            return {
                "success": True,
                "file_path": file_path,
                "uploaded_bytes": uploaded_bytes,
            }
            
        except Exception as e:
            logger.error(f"Upload failed: {e}")
            raise
    
    async def _perform_download(self, task_id: str, file_url: str, local_path: str, progress_callback: Optional[Callable]) -> Dict[str, Any]:
        """执行文件下载"""
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(file_url) as response:
                    if response.status != 200:
                        raise Exception(f"下载失败: HTTP {response.status}")
                    
                    total_size = int(response.headers.get('content-length', 0))
                    downloaded_bytes = 0
                    
                    # 确保目录存在
                    local_path_obj = Path(local_path)
                    local_path_obj.parent.mkdir(parents=True, exist_ok=True)
                    
                    async with aiofiles.open(local_path, 'wb') as file:
                        async for chunk in response.content.iter_chunked(self.chunk_size):
                            await file.write(chunk)
                            downloaded_bytes += len(chunk)
                            
                            if total_size > 0:
                                progress = int((downloaded_bytes / total_size) * 100)
                            else:
                                progress = 0
                            
                            # 更新任务进度
                            if task_id in self.download_tasks:
                                self.download_tasks[task_id]["progress"] = progress
                            
                            # 调用进度回调
                            if progress_callback:
                                if asyncio.iscoroutinefunction(progress_callback):
                                    await progress_callback(progress, f"下载中... {progress}%")
                                else:
                                    progress_callback(progress, f"下载中... {progress}%")
                            
                            # 通知回调
                            await self._notify_download_callbacks("progress_updated", {
                                "task_id": task_id,
                                "progress": progress,
                                "downloaded_bytes": downloaded_bytes,
                                "total_bytes": total_size,
                            })
            
            return {
                "success": True,
                "local_path": local_path,
                "file_size": downloaded_bytes,
            }
            
        except Exception as e:
            logger.error(f"Download failed: {e}")
            raise
    
    async def _calculate_file_hash(self, file_path: str) -> str:
        """计算文件哈希"""
        hash_md5 = hashlib.md5()
        async with aiofiles.open(file_path, 'rb') as f:
            while chunk := await f.read(self.chunk_size):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
    
    async def _read_text_preview(self, file_path: str, max_lines: int = 10) -> str:
        """读取文本文件预览"""
        try:
            lines = []
            async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
                for i, line in enumerate(await f.readlines()):
                    if i >= max_lines:
                        break
                    lines.append(line.rstrip())
            return '\n'.join(lines)
        except Exception:
            return "无法读取文本内容"
    
    def _format_file_size(self, size_bytes: int) -> str:
        """格式化文件大小"""
        for unit in ['B', 'KB', 'MB', 'GB']:
            if size_bytes < 1024.0:
                return f"{size_bytes:.1f} {unit}"
            size_bytes /= 1024.0
        return f"{size_bytes:.1f} TB"
    
    # 回调管理
    def add_upload_callback(self, callback: Callable) -> None:
        """添加上传回调"""
        self.upload_callbacks.append(callback)
    
    def remove_upload_callback(self, callback: Callable) -> None:
        """移除上传回调"""
        self.upload_callbacks = [cb for cb in self.upload_callbacks if cb != callback]
    
    def add_download_callback(self, callback: Callable) -> None:
        """添加下载回调"""
        self.download_callbacks.append(callback)
    
    def remove_download_callback(self, callback: Callable) -> None:
        """移除下载回调"""
        self.download_callbacks = [cb for cb in self.download_callbacks if cb != callback]
    
    async def _notify_upload_callbacks(self, event_type: str, data: Dict[str, Any]) -> None:
        """通知上传回调"""
        for callback in self.upload_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(event_type, data)
                else:
                    callback(event_type, data)
            except Exception as e:
                logger.error(f"Upload callback error: {e}")
    
    async def _notify_download_callbacks(self, event_type: str, data: Dict[str, Any]) -> None:
        """通知下载回调"""
        for callback in self.download_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(event_type, data)
                else:
                    callback(event_type, data)
            except Exception as e:
                logger.error(f"Download callback error: {e}")
    
    # 任务管理
    def get_upload_task(self, task_id: str) -> Optional[Dict[str, Any]]:
        """获取上传任务"""
        return self.upload_tasks.get(task_id)
    
    def get_download_task(self, task_id: str) -> Optional[Dict[str, Any]]:
        """获取下载任务"""
        return self.download_tasks.get(task_id)
    
    def get_all_upload_tasks(self) -> List[Dict[str, Any]]:
        """获取所有上传任务"""
        return list(self.upload_tasks.values())
    
    def get_all_download_tasks(self) -> List[Dict[str, Any]]:
        """获取所有下载任务"""
        return list(self.download_tasks.values())
    
    def cancel_upload_task(self, task_id: str) -> bool:
        """取消上传任务"""
        if task_id in self.upload_tasks:
            self.upload_tasks[task_id]["status"] = "cancelled"
            return True
        return False
    
    def cancel_download_task(self, task_id: str) -> bool:
        """取消下载任务"""
        if task_id in self.download_tasks:
            self.download_tasks[task_id]["status"] = "cancelled"
            return True
        return False
    
    def clear_completed_tasks(self) -> None:
        """清除已完成的任务"""
        # 清除已完成的上传任务
        completed_uploads = [
            task_id for task_id, task in self.upload_tasks.items()
            if task["status"] in ["completed", "failed", "cancelled"]
        ]
        for task_id in completed_uploads:
            del self.upload_tasks[task_id]
        
        # 清除已完成的下载任务
        completed_downloads = [
            task_id for task_id, task in self.download_tasks.items()
            if task["status"] in ["completed", "failed", "cancelled"]
        ]
        for task_id in completed_downloads:
            del self.download_tasks[task_id]

